﻿using Microsoft.Extensions.Caching.Memory;
using PmSoft.Core.Domain.Entities;
using System.Linq.Expressions;

namespace PmSoft.Cache.Abstractions.EntityCache;

/// <summary>
/// 基于运行时内存缓存的实体缓存版本服务类
/// </summary>
/// <typeparam name="TEntity">实体类型，必须实现 IEntity<TKey></typeparam>
/// <typeparam name="TKey">实体主键类型，不可为空</typeparam>
public class MemoryEntityCacheVersionService<TEntity, TKey> : IEntityCacheVersionService<TEntity, TKey>
	where TEntity : IEntity<TKey> where TKey : notnull
{
	private readonly ICacheService _cacheService;
	private readonly IMemoryCache _memoryCache;

	private const string EntityCachePrefix = "entitys";           // 实体缓存键前缀
	private const string MetadataCachePrefix = "metadata_cache_version"; // 元数据缓存键前缀

	/// <summary>
	/// 构造函数，初始化缓存服务和内存缓存
	/// </summary>
	/// <param name="cacheService">缓存服务接口</param>
	/// <param name="memoryCache">内存缓存实例</param>
	public MemoryEntityCacheVersionService(ICacheService cacheService, IMemoryCache memoryCache)
	{
		_cacheService = cacheService ?? throw new ArgumentNullException(nameof(cacheService));
		_memoryCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
		Metadata = CacheEntityMetadata.ForType(typeof(TEntity), _cacheService.EnableDistributedCache);
	}

	public IEntityMetadata Metadata { get; }

	public bool IsDistributed => false;

	/// <summary>
	/// 获取单个实体的缓存键（同步）
	/// </summary>
	/// <param name="entityId">实体ID</param>
	public string GetCacheKeyOfEntity(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		return $"{EntityCachePrefix}:{Metadata.Name}:{entityId}";
	}

	/// <summary>
	/// 获取单个实体的缓存键（异步）
	/// </summary>
	/// <param name="entityId">实体ID</param>
	public ValueTask<string> GetCacheKeyOfEntityAsync(TKey entityId)
	{
		return new ValueTask<string>(GetCacheKeyOfEntity(entityId));
	}

	/// <summary>
	/// 获取多个实体的缓存键（同步）
	/// </summary>
	/// <param name="entityIds">实体ID集合</param>
	public Dictionary<string, string> GetCacheKeyOfEntitys(IEnumerable<TKey> entityIds)
	{
		if (entityIds == null) throw new ArgumentNullException(nameof(entityIds));
		return entityIds.ToDictionary(
			id => id.ToString() ?? string.Empty,
			GetCacheKeyOfEntity
		);
	}

	/// <summary>
	/// 获取多个实体的缓存键（异步）
	/// </summary>
	/// <param name="entityIds">实体ID集合</param>
	public ValueTask<Dictionary<string, string>> GetCacheKeyOfEntitysAsync(IEnumerable<TKey> entityIds)
	{
		return new ValueTask<Dictionary<string, string>>(GetCacheKeyOfEntitys(entityIds));
	}

	/// <summary>
	/// 获取实体版本号（同步）
	/// </summary>
	/// <param name="entityId">实体ID</param>
	public int GetEntityVersion(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		string key = $"{GetEntityVersionHashKey()}:{entityId}";
		return _memoryCache.TryGetValue(key, out int version) ? version : 0;
	}

	/// <summary>
	/// 获取实体版本号（异步）
	/// </summary>
	/// <param name="entityId">实体ID</param>
	public ValueTask<int> GetEntityVersionAsync(TKey entityId)
	{
		return new ValueTask<int>(GetEntityVersion(entityId));
	}

	//// <summary>
	/// 获取区域版本号（同步），基于实体属性选择器
	/// </summary>
	/// <typeparam name="TAreaKey">区域键类型</typeparam>
	/// <param name="areaKeySelector">实体属性选择表达式，用于指定区域键</param>
	/// <param name="propertyValue">属性值，若为空则返回 0</param>
	/// <returns>区域版本号，若未找到则返回 0</returns>
	public int GetAreaVersion<TAreaKey>(Expression<Func<TEntity, TAreaKey>> areaKeySelector, object? propertyValue)
	{
		if (areaKeySelector == null) throw new ArgumentNullException(nameof(areaKeySelector));
		if (propertyValue == null) return 0;

		string propertyName = ExtractPropertyName(areaKeySelector);
		string key = $"{GetAreaVersionHashKey(propertyName)}:{propertyValue}";
		return _memoryCache.TryGetValue(key, out int version) ? version : 0;
	}

	/// <summary>
	/// 获取区域版本号（同步），基于实体属性选择器
	/// </summary>
	/// <typeparam name="TAreaKey">区域键类型</typeparam>
	/// <param name="areaKeySelector">实体属性选择表达式，用于指定区域键</param>
	/// <param name="propertyValue">属性值，若为空则返回 0</param>
	/// <returns>区域版本号，若未找到则返回 0</returns>
	public ValueTask<int> GetAreaVersionAsync<TAreaKey>(Expression<Func<TEntity, TAreaKey>> areaKeySelector, object? propertyValue)
	{
		return new ValueTask<int>(GetAreaVersion(areaKeySelector, propertyValue));
	}

	/// <summary>
	/// 从属性选择表达式中提取属性名称
	/// </summary>
	/// <typeparam name="TAreaKey">属性类型</typeparam>
	/// <param name="selector">属性选择表达式</param>
	/// <returns>属性名称</returns>
	private string ExtractPropertyName<TAreaKey>(Expression<Func<TEntity, TAreaKey>> selector)
	{
		var expr = selector.Body as MemberExpression;
		if (expr == null && selector.Body is UnaryExpression unaryExpr)
		{
			expr = unaryExpr.Operand as MemberExpression;
		}

		string propertyName = expr?.Member.Name ?? throw new ArgumentException("无法从选择表达式中提取属性名", nameof(selector));
		if (!Metadata.PropertiesOfArea!.Any(m => m.Name == propertyName))
			throw new InvalidOperationException($"{Metadata.Name}实体的缓存属性未配置属性名为“{propertyName}”的缓存区域");
		return propertyName;
	}
	/// <summary>
	/// 获取全局版本号（同步）
	/// </summary>
	public int GetGlobalVersion()
	{
		string key = $"{GetGlobalVersionHashKey()}:{Metadata.Name}";
		return _memoryCache.TryGetValue(key, out int version) ? version : 0;
	}

	/// <summary>
	/// 获取全局版本号（异步）
	/// </summary>
	public ValueTask<int> GetGlobalVersionAsync()
	{
		return new ValueTask<int>(GetGlobalVersion());
	}

	/// <summary>
	/// 递增全局版本号（同步）
	/// </summary>
	public void IncreaseGlobalVersion()
	{
		string key = $"{GetGlobalVersionHashKey()}:{Metadata.Name}";
		_memoryCache.Set(key, GetGlobalVersion() + 1);
	}

	/// <summary>
	/// 递增全局版本号（异步）
	/// </summary>
	public Task IncreaseGlobalVersionAsync()
	{
		IncreaseGlobalVersion();
		return Task.CompletedTask;
	}

	/// <summary>
	/// 递增区域版本号（同步，多个值）
	/// </summary>
	/// <param name="propertyName">属性名称</param>
	/// <param name="propertyValues">属性值集合</param>
	public void IncreaseAreaVersion(string propertyName, IEnumerable<object> propertyValues)
	{
		if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException(nameof(propertyName));
		if (propertyValues == null) throw new ArgumentNullException(nameof(propertyValues));
		string baseKey = GetAreaVersionHashKey(propertyName);
		foreach (var value in propertyValues)
		{
			string key = $"{baseKey}:{value}";
			_memoryCache.Set(key, (_memoryCache.TryGetValue(key, out int v) ? v : 0) + 1);
		}
	}

	/// <summary>
	/// 递增区域版本号（异步，多个值）
	/// </summary>
	/// <param name="propertyName">属性名称</param>
	/// <param name="propertyValues">属性值集合</param>
	public Task IncreaseAreaVersionAsync(string propertyName, IEnumerable<object> propertyValues)
	{
		IncreaseAreaVersion(propertyName, propertyValues);
		return Task.CompletedTask;
	}

	/// <summary>
	/// 递增区域版本号（同步，单个值）
	/// </summary>
	/// <param name="propertyName">属性名称</param>
	/// <param name="propertyValue">属性值</param>
	public void IncreaseAreaVersion(string propertyName, object propertyValue)
	{
		if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException(nameof(propertyName));
		if (propertyValue == null) throw new ArgumentNullException(nameof(propertyValue));
		string key = $"{GetAreaVersionHashKey(propertyName)}:{propertyValue}";
		_memoryCache.Set(key, (_memoryCache.TryGetValue(key, out int v) ? v : 0) + 1);
	}

	/// <summary>
	/// 递增区域版本号（异步，单个值）
	/// </summary>
	/// <param name="propertyName">属性名称</param>
	/// <param name="propertyValue">属性值</param>
	public Task IncreaseAreaVersionAsync(string propertyName, object propertyValue)
	{
		IncreaseAreaVersion(propertyName, propertyValue);
		return Task.CompletedTask;
	}

	/// <summary>
	/// 递增实体版本号（同步）
	/// </summary>
	/// <param name="entityId">实体ID</param>
	public void IncreaseEntityCacheVersion(TKey entityId)
	{
		if (entityId == null) throw new ArgumentNullException(nameof(entityId));
		string key = $"{GetEntityVersionHashKey()}:{entityId}";
		_memoryCache.Set(key, GetEntityVersion(entityId) + 1);
	}

	/// <summary>
	/// 递增实体版本号（异步）
	/// </summary>
	/// <param name="entityId">实体ID</param>
	public Task IncreaseEntityCacheVersionAsync(TKey entityId)
	{
		IncreaseEntityCacheVersion(entityId);
		return Task.CompletedTask;
	}

	/// <summary>
	/// 递增列表缓存版本号（同步）
	/// </summary>
	/// <param name="entity">实体对象</param>
	public void IncreaseListCacheVersion(IEntity<TKey> entity)
	{
		if (Metadata.PropertiesOfArea != null)
		{
			foreach (var info in Metadata.PropertiesOfArea)
			{
				if (info.GetValue(entity) is object propertyValue)
				{
					IncreaseAreaVersion(info.Name.ToLower(), propertyValue);
				}
			}
		}
		IncreaseGlobalVersion();
	}

	/// <summary>
	/// 递增列表缓存版本号（异步）
	/// </summary>
	/// <param name="entity">实体对象</param>
	public Task IncreaseListCacheVersionAsync(IEntity<TKey> entity)
	{
		IncreaseListCacheVersion(entity);
		return Task.CompletedTask;
	}

	#region 私有方法

	/// <summary>
	/// 获取全局版本号的键前缀
	/// </summary>
	private string GetGlobalVersionHashKey() => $"{MetadataCachePrefix}:global_version".ToLower();

	/// <summary>
	/// 获取区域版本号的键前缀
	/// </summary>
	/// <param name="propertyName">属性名称</param>
	private string GetAreaVersionHashKey(string propertyName) =>
		$"{MetadataCachePrefix}:area_version:{Metadata.Name}:{propertyName}".ToLower();

	/// <summary>
	/// 获取实体版本号的键前缀
	/// </summary>
	private string GetEntityVersionHashKey() => $"{MetadataCachePrefix}:entity_version:{Metadata.Name}".ToLower();

	#endregion
}